//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

namespace Microsoft.Research.Provenance.Controls.Presenter
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Microsoft.Research.Provenance.Controls.View;
    using Microsoft.Practices.Unity;
    using Microsoft.Research.ScientificWorkflow.TridentModel;
    using Microsoft.Research.ScientificWorkflow.TridentUtilities;
    using SR = Microsoft.Research.DataLayer;
    using System.Windows.Input;
    using Microsoft.Research.ScientificWorkflow.UIDesigner.WpfComposite;
    using Microsoft.Research.ScientificWorkflow.UIDesigner.Monitor;
    using Microsoft.Research.ProvenanceInterfaces;
    using System.Collections.ObjectModel;
    using Microsoft.Research.Provenance.ProvenanceDataService;
    using System.Windows;
    using System.ComponentModel;

    /// <summary>
    /// Presentation logic for displaying detailed information of 
    /// the workflow provenance details.
    /// </summary>
    public class DetailedProvenancePresenter : IDisposable, INotifyPropertyChanged
    {
        #region Private fields
        /// <summary>
        /// The unity container instance.
        /// </summary>
        private IUnityContainer container;

        /// <summary>
        /// The dictionary of all available presenters.
        /// </summary>
        private Dictionary<String, IProvenancePresenter> provenancePresenters = new Dictionary<string, IProvenancePresenter>();

        /// <summary>
        /// The model of the workflow to be displayed in the window.
        /// </summary>
        private TridentWorkflowModel workflowModel;
        #endregion

        /// <summary>
        /// The command displays Data products.
        /// </summary>
        public ICommand GetProvenanceDetail { get; private set; }

        /// <summary>
        /// This is to check whether the view is updated or not.
        /// </summary>
        public bool IsUpdated { get; private set; }

        /// <summary>
        /// This is used to set the expanding behaviour of the view.
        /// </summary>
        private bool isExpanded;

        /// <summary>
        /// This is used to call the utility functions defined in DataRetriever class.
        /// </summary>
        DataRetriever dataRetriever;

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="container">The unity container.</param>
        /// <param name="view">The view.</param>
        /// <param name="jobID">The job id.</param>
        public DetailedProvenancePresenter(
            IUnityContainer container,
            DetailProvenanceInformationView view)
        {
            this.container = container;
            this.View = view;

            this.View.Presenter = this;

            this.InitializeCommands();

            this.InitializePresenters();
        }

        #region Public properties
        /// <summary>
        /// The workflow name.
        /// </summary>
        public string WorkflowName { get; private set; }

        /// <summary>
        /// The command displays processing status view.
        /// </summary>
        public ICommand DisplayProcessingStatus { get; private set; }

        /// <summary>
        /// The command displays input output status view.
        /// </summary>
        public ICommand DisplayInputOutput { get; private set; }

        /// <summary>
        /// The command displays Data products view.
        /// </summary>
        public ICommand DisplayDataProducts { get; private set; }

        /// <summary>
        /// The command displays performance view.
        /// </summary>
        public ICommand DisplayPerformance { get; private set; }

        /// <summary>
        /// The current presenter which is in view.
        /// </summary>
        public IProvenancePresenter CurrentPresenter { get; private set; }

        /// <summary>
        /// Gets or sets the IsExpanded property.
        /// </summary>
        public bool IsExpanded
        {
            get
            {
                return this.isExpanded;
            }
            set
            {
                this.isExpanded = value;
                this.OnNotifyPropertyChanged("IsExpanded");
            }
        }

        /// <summary>
        /// The execute workflow usercontrol.
        /// </summary>
        public DetailProvenanceInformationView View { get; set; }


        #endregion

        #region Public methods
        /// <summary>
        /// Load the user control with workflow.
        /// </summary>
        public void UpdateDetailedProvenance()
        {
            try
            {
                this.CreateWorkflowModel();

                this.WorkflowName = this.workflowModel.Name;

                this.View.Initialize(this.workflowModel, this.container.Resolve<Dictionary<string, DataTypeModel>>("Datatypes"));

                // Update the provenance info from the provenance table.
                this.container.Resolve<DataRetriever>().GetProvenanceInfo();

                this.IsUpdated = true;

                // Default : Set the root activity and then set the detailed information into processing status
                this.ShowDetails(this.provenancePresenters["ProcessingStatus"], this.workflowModel.Root.UniqueId);
            }
            catch (TridentCustomException ex)
            {
                TridentMessageBox.ShowTridentErrorMessageBox(ex.Message);
            }
            catch (Exception exp)
            {
                TridentErrorHandler.HandleKnownExceptionsInUI(exp);
            }
        }
        #endregion

        #region Private methods
        /// <summary>
        /// Initialize the commands.
        /// </summary>
        private void InitializeCommands()
        {
            this.DisplayProcessingStatus = new DelegateCommand<string>(RenderProcessingStatus);
            this.DisplayInputOutput = new DelegateCommand<string>(RenderInputOutput);
            this.DisplayDataProducts = new DelegateCommand<string>(RenderDataProducts);
            this.DisplayPerformance = new DelegateCommand<string>(RenderPerformance);

            this.GetProvenanceDetail = new DelegateCommand<object>(p => { this.UpdateDetailedProvenance(); });
        }

        /// <summary>
        /// Initialize the presenters.
        /// </summary>
        private void InitializePresenters()
        {
            this.provenancePresenters.Add("ProcessingStatus", this.container.Resolve<ActivityProcessingStatusPresenter>());
            this.provenancePresenters.Add("InputOutPut", this.container.Resolve<ActivityInputOutputPresenter>());
            this.provenancePresenters.Add("DataProduct", this.container.Resolve<ActivityDataProductPresenter>());
            this.provenancePresenters.Add("Performance", this.container.Resolve<ActivityPerformancePresenter>());

            foreach (IProvenancePresenter presenter in this.provenancePresenters.Values)
            {
                this.View.AddViews(presenter.View);
                presenter.View.Visibility = Visibility.Collapsed;
            }
        }

        /// <summary>
        /// Collapse all views.
        /// </summary>
        private void HideAllViews()
        {
            foreach (IProvenancePresenter presenter in this.container.ResolveAll<IProvenancePresenter>())
            {
                presenter.Hide();
            }
        }

        /// <summary>
        /// Show the required presenter.
        /// </summary>
        /// <param name="presenter">The presenter to display.</param>
        /// <param name="activityName">The activity name.</param>
        private void ShowDetails(IProvenancePresenter presenter, string activityName)
        {
            if (this.CurrentPresenter != null)
            {
                this.CurrentPresenter.Hide();
            }

            if (workflowModel.ModelsHashtable.Contains(activityName))
            {
                // Get the activity label.
                BaseModel activityModel = workflowModel.ModelsHashtable[activityName] as BaseModel;
                presenter.Show(activityModel);
                this.CurrentPresenter = presenter;
            }
        }

        /// <summary>
        /// Show the performance details.
        /// </summary>
        /// <param name="activityName">The activity name.</param>
        private void RenderPerformance(string activityName)
        {
            this.ShowDetails(this.provenancePresenters["Performance"], activityName);
        }

        /// <summary>
        /// Show the data products of an activity.
        /// </summary>
        /// <param name="activityName">The activity name.</param>
        private void RenderDataProducts(string activityName)
        {
            this.ShowDetails(this.provenancePresenters["DataProduct"], activityName);
        }

        /// <summary>
        /// Show the Input output parameters of the activity.
        /// </summary>
        /// <param name="activityName">The activity name.</param>
        private void RenderInputOutput(string activityName)
        {
            this.ShowDetails(this.provenancePresenters["InputOutPut"], activityName);
        }

        /// <summary>
        /// Show the processing status details of the activity.
        /// </summary>
        /// <param name="activityName">The activity name.</param>
        private void RenderProcessingStatus(string activityName)
        {
            this.ShowDetails(this.provenancePresenters["ProcessingStatus"], activityName);
        }

        /// <summary>
        /// Create the workflow model based on the workflow ID.
        /// </summary>
        private void CreateWorkflowModel()
        {
            dataRetriever = this.container.Resolve<DataRetriever>();

            this.workflowModel = dataRetriever.GetWorkflow();

            this.AttachHandler(this.workflowModel.Root);
        }

        /// <summary>
        /// Attaches the handler to notify index changed in combobox of iterative activity.
        /// </summary>
        /// <param name="compositeElement">The composite element.</param>
        private void AttachHandler(BaseCompositeModel compositeElement)
        {
            compositeElement.IndexChangedEvent += new EventHandler(this.IterativeElementIndexChangedEvent);
            foreach (BaseModel child in compositeElement.Children)
            {
                if (child is BaseCompositeModel)
                    AttachHandler(child as BaseCompositeModel);
            }
        }

        /// <summary>
        /// Iteratives the element index changed event.
        /// </summary>
        /// <param name="from">From.</param>
        /// <param name="args">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void IterativeElementIndexChangedEvent(object sender, EventArgs args)
        {
            Collection<string> Iterations = null;

            if (sender is CompositeActivityModel)
            {
                if ((sender as BaseCompositeModel).Name == "Replicator")
                {
                    sender = (sender as BaseCompositeModel).Children[0];

                }

                Iterations = new Collection<string>(dataRetriever.GetIterationValues((sender as BaseCompositeModel).UniqueId));
                (sender as BaseCompositeModel).IterationValues = Iterations;
            }
        }

        #endregion

        #region IDisposable Members

        /// <summary>
        /// Clears the collection and View Object
        /// </summary>
        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// nullify and Disposes the object 
        /// </summary>
        /// <param name="disposing"></param>
        private void Dispose(bool disposing)
        {
            if (disposing)
            {
                this.container.Dispose();
                this.View = null;
            }
        }

        #endregion

        #region INotifyPropertyChanged Members

        /// <summary>
        /// Event for property change.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Raise property changed eventargs.
        /// </summary>
        /// <param name="changeProperty">Property anme.</param>
        private void OnNotifyPropertyChanged(string changeProperty)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs(changeProperty));
            }
        }

        #endregion
    }
}
